package com.hsjk.szwj.module.pay.controller.app.refund;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hsjk.szwj.framework.common.exception.ServiceException;
import com.hsjk.szwj.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.hsjk.szwj.framework.common.util.spring.SpringBeansUtil;
import com.hsjk.szwj.framework.common.util.string.StringKit;
import com.hsjk.szwj.module.pay.constant.PayOrderConstant;
import com.hsjk.szwj.module.pay.constant.RefundOrderConstant;
import com.hsjk.szwj.module.pay.controller.PayApiController;
import com.hsjk.szwj.module.pay.controller.app.refund.vo.RefundOrderReqVO;
import com.hsjk.szwj.module.pay.controller.app.refund.vo.RefundOrderRespVO;
import com.hsjk.szwj.module.pay.dal.dataobject.CommonPayResult;
import com.hsjk.szwj.module.pay.dal.dataobject.config.ChannelRetMsg;
import com.hsjk.szwj.module.pay.dal.dataobject.config.MchAppConfigContext;
import com.hsjk.szwj.module.pay.dal.dataobject.order.PayOrderDO;
import com.hsjk.szwj.module.pay.dal.dataobject.order.RefundOrderDO;
import com.hsjk.szwj.module.pay.dal.dataobject.system.MchAppDO;
import com.hsjk.szwj.module.pay.dal.dataobject.system.MchInfoDO;
import com.hsjk.szwj.module.pay.exception.ChannelException;
import com.hsjk.szwj.module.pay.service.ConfigContextQueryService;
import com.hsjk.szwj.module.pay.service.IMchAppService;
import com.hsjk.szwj.module.pay.service.IMchInfoService;
import com.hsjk.szwj.module.pay.service.IRefundService;
import com.hsjk.szwj.module.pay.service.PayInterfaceDefineService;
import com.hsjk.szwj.module.pay.service.PayMchNotifyService;
import com.hsjk.szwj.module.pay.service.PayOrderService;
import com.hsjk.szwj.module.pay.service.RefundOrderService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author chenjiawei
 * @desc 商户发起退款
 * @Date 2023/08/24 18:32
 **/
@Tag(name = "应用端 - 退款相关接口")
@RestController
@RequestMapping("/refund")
@Validated
@Slf4j
public class RefundOrderController extends PayApiController {
    @Resource
    private PayOrderService payOrderService;
    @Resource
    private RefundOrderService refundOrderService;
    @Resource
    private PayMchNotifyService payMchNotifyService;
    @Resource
    private ConfigContextQueryService configContextQueryService;
    @Resource
    private PayInterfaceDefineService payInterfaceDefineService;
    @Resource
    private IMchInfoService mchInfoService;
    @Resource
    private IMchAppService mchAppService;

    /**
     * 申请退款
     **/
    @PostMapping("/refundOrder")
    public CommonPayResult refundOrder() {


        RefundOrderDO refundOrder = null;

        //获取参数 & 验签
        RefundOrderReqVO rq = getRQByWithMchSign(RefundOrderReqVO.class);

        try {

            if (ObjectUtil.isNull(rq.getMchOrderId()) || ObjectUtil.isNull(rq.getPayOrderId())) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "mchOrderid 和 payOrderId不能同时为空");
            }

            if (StringUtils.isNotEmpty(rq.getNotifyUrl()) &&
                    !StringKit.isAvailableUrl(rq.getNotifyUrl())) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "异步通知地址协议仅支持http:// 或 https:// !");
            }
            MchInfoDO mchInfoDO = mchInfoService.getMchInfoById(rq.getMchId());
            PayOrderDO payOrder = payOrderService.queryMchOrder(mchInfoDO.getId(),
                    rq.getPayOrderId(), rq.getMchOrderId());
            if (payOrder == null) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "退款订单不存在");
            }

            if (payOrder.getState() != PayOrderConstant.STATE_SUCCESS) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "订单状态不正确， 无法完成退款");
            }

            if (payOrder.getRefundState() == PayOrderConstant.REFUND_STATE_ALL
                    || payOrder.getRefundAmount() >= payOrder.getAmount()) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "订单已全额退款，本次申请失败");
            }

            if (payOrder.getRefundAmount() + rq.getRefundAmount() > payOrder.getAmount()) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "申请金额超出订单可退款余额，请检查退款金额");
            }

            if (refundOrderService.count(new LambdaQueryWrapper<RefundOrderDO>()
                    .eq(RefundOrderDO::getPayOrderId,
                            payOrder.getId()).
                            eq(RefundOrderDO::getState, RefundOrderConstant.STATE_ING)) > 0) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "支付订单具有在途退款申请，请稍后再试");
            }

            //全部退款金额 （退款订单表）
            Long sumSuccessRefundAmount = refundOrderService.getBaseMapper()
                    .sumSuccessRefundAmount(payOrder.getId());
            if (sumSuccessRefundAmount >= payOrder.getAmount()) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "退款单已完成全部订单退款，本次申请失败");
            }

            if (sumSuccessRefundAmount + rq.getRefundAmount() > payOrder.getAmount()) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "申请金额超出订单可退款余额，请检查退款金额");
            }

            Long appId = rq.getAppId();

            // 校验退款单号是否重复
            if (refundOrderService.count(new LambdaQueryWrapper<RefundOrderDO>()
                    .eq(RefundOrderDO::getMchId, mchInfoDO.getId())
                    .eq(RefundOrderDO::getMchRefundId, rq.getMchRefundId())) > 0) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "商户退款订单号[" + rq.getMchRefundId() + "]已存在");
            }

            //获取支付参数 (缓存数据) 和 商户信息
            MchAppConfigContext mchAppConfigContext =
                    configContextQueryService.queryMchInfoAndAppInfo(rq.getMchId(), appId);
            if (mchAppConfigContext == null) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "获取商户应用信息失败");
            }

            MchInfoDO mchInfo = mchAppConfigContext.getMchInfo();
            MchAppDO mchApp = mchAppConfigContext.getMchApp();
            String ifCode = payInterfaceDefineService.getCodeById(payOrder.getIfId());
            //获取退款接口
            IRefundService refundService =
                    SpringBeansUtil.getBean(ifCode + "RefundService", IRefundService.class);
            if (refundService == null) {
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "当前通道不支持退款！");
            }

            refundOrder = genRefundOrder(rq, payOrder, mchInfo, mchApp);

            //退款单入库 退款单状态：生成状态  此时没有和任何上游渠道产生交互。
            refundOrderService.save(refundOrder);

            // 调起退款接口
            ChannelRetMsg channelRetMsg = refundService.refund(rq, refundOrder, payOrder, mchAppConfigContext);


            //处理退款单状态
            this.processChannelMsg(channelRetMsg, refundOrder);

            RefundOrderRespVO bizRes = RefundOrderRespVO.buildByRefundOrder(refundOrder);
            return CommonPayResult.okWithSign(bizRes,
                    configContextQueryService.queryMchApp(rq.getMchId(),
                            rq.getAppId()).getAppSecret());


        } catch (ServiceException e) {
            return CommonPayResult.error(e.getMessage());

        } catch (ChannelException e) {

            //处理上游返回数据
            this.processChannelMsg(e.getChannelRetMsg(), refundOrder);

            if (e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR) {
                return CommonPayResult.error(e.getMessage());
            }

            RefundOrderRespVO bizRes = RefundOrderRespVO.buildByRefundOrder(refundOrder);
            return CommonPayResult.okWithSign(bizRes,
                    configContextQueryService.queryMchApp(rq.getMchId(),
                            rq.getAppId()).getAppSecret());


        } catch (Exception e) {
            log.error("系统异常：{}", e);
            return CommonPayResult.error("系统异常");
        }

    }

    private RefundOrderDO genRefundOrder(RefundOrderReqVO rq,
                                         PayOrderDO payOrder,
                                         MchInfoDO mchInfo, MchAppDO mchApp) {

        Date nowTime = new Date();
        RefundOrderDO refundOrder = new RefundOrderDO();
        //支付订单号
        refundOrder.setPayOrderId(payOrder.getId());
        //渠道支付单号
        refundOrder.setChannelPayOrderNo(payOrder.getChannelOrderNo());
        //商户号
        refundOrder.setMchId(mchInfo.getId());
        //服务商号
        refundOrder.setIsvId(mchInfo.getIsvId());
        //商户应用ID
        refundOrder.setAppId(mchApp.getId());
        //商户名称
        refundOrder.setMchName(mchInfo.getMchShortName());
        //商户类型
        refundOrder.setMchType(mchInfo.getMchType());
        //商户退款单号
        refundOrder.setMchRefundId(rq.getMchRefundId());
        //支付方式代码
        refundOrder.setWayCode(payOrder.getWayCode());
        //支付接口代码
        refundOrder.setIfId(payOrder.getIfId());
        //支付金额,单位分
        refundOrder.setPayAmount(payOrder.getAmount());
        //退款金额,单位分
        refundOrder.setRefundAmount(rq.getRefundAmount());
        //三位货币代码,人民币:cny
        refundOrder.setCurrency(rq.getCurrency());
        //退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败
        refundOrder.setState(RefundOrderConstant.STATE_INIT);
        //客户端IP
        refundOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), getClientIp()));
        //退款原因
        refundOrder.setRefundReason(rq.getRefundReason());
        //渠道订单号
        refundOrder.setChannelOrderNo(null);
        //渠道错误码
        refundOrder.setErrCode(null);
        //渠道错误描述
        refundOrder.setErrMsg(null);
        //特定渠道发起时额外参数
        refundOrder.setChannelExtra(rq.getChannelExtra());
        //通知地址
        refundOrder.setNotifyUrl(rq.getNotifyUrl());
        //扩展参数
        refundOrder.setExtParam(rq.getExtParam());
        //订单超时关闭时间 默认两个小时
        refundOrder.setExpiredTime(LocalDateTime.now().plusHours(2));
        //订单退款成功时间
        refundOrder.setSuccessTime(null);
        //创建时间
        refundOrder.setCreateTime(LocalDateTime.now());

        return refundOrder;
    }


    /**
     * 处理返回的渠道信息，并更新退款单状态
     * payOrder将对部分信息进行 赋值操作。
     **/
    private void processChannelMsg(ChannelRetMsg channelRetMsg,
                                   RefundOrderDO refundOrder) {

        //对象为空 || 上游返回状态为空， 则无需操作
        if (channelRetMsg == null || channelRetMsg.getChannelState() == null) {
            return;
        }

        //明确成功
        if (ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {

            this.updateInitOrderStateThrowException(RefundOrderConstant.STATE_SUCCESS, refundOrder, channelRetMsg);
            payMchNotifyService.refundOrderNotify(refundOrder);

            //明确失败
        } else if (ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {

            this.updateInitOrderStateThrowException(RefundOrderConstant.STATE_FAIL, refundOrder, channelRetMsg);
            payMchNotifyService.refundOrderNotify(refundOrder);

            // 上游处理中 || 未知 || 上游接口返回异常  退款单为退款中状态
        } else if (ChannelRetMsg.ChannelState.WAITING == channelRetMsg.getChannelState() ||
                ChannelRetMsg.ChannelState.UNKNOWN == channelRetMsg.getChannelState() ||
                ChannelRetMsg.ChannelState.API_RET_ERROR == channelRetMsg.getChannelState()

        ) {
            this.updateInitOrderStateThrowException(RefundOrderConstant.STATE_ING, refundOrder, channelRetMsg);

            // 系统异常：  退款单不再处理。  为： 生成状态
        } else if (ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState()) {

        } else {

            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                    "ChannelState 返回异常！");
        }

    }


    /**
     * 更新退款单状态 --》 退款单生成--》 其他状态  (向外抛出异常)
     **/
    private void updateInitOrderStateThrowException(Integer orderState,
                                                    RefundOrderDO refundOrder,
                                                    ChannelRetMsg channelRetMsg) {

        refundOrder.setState(orderState);
        refundOrder.setChannelOrderNo(channelRetMsg.getChannelOrderId());
        refundOrder.setErrCode(channelRetMsg.getChannelErrCode());
        refundOrder.setErrMsg(channelRetMsg.getChannelErrMsg());


        boolean isSuccess = refundOrderService.updateInit2Ing(refundOrder.getIfId(),
                channelRetMsg.getChannelOrderId());
        if (!isSuccess) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                    "更新退款单异常!");
        }

        isSuccess = refundOrderService
                .updateIng2SuccessOrFail(refundOrder.getIfId(), refundOrder.getState(),
                        channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrCode(), channelRetMsg.getChannelErrMsg());
        if (!isSuccess) {
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                    "更新退款单异常!");
        }
    }
}
